package com.itheima.pinda.file.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.pinda.base.entity.SuperEntity;
import com.itheima.pinda.base.id.IdGenerate;
import com.itheima.pinda.database.mybatis.conditions.Wraps;
import com.itheima.pinda.dozer.DozerUtils;
import com.itheima.pinda.exception.BizException;
import com.itheima.pinda.file.biz.FileBiz;
import com.itheima.pinda.file.dao.AttachmentMapper;
import com.itheima.pinda.file.domain.FileDO;
import com.itheima.pinda.file.domain.FileDeleteDO;
import com.itheima.pinda.file.dto.AttachmentDTO;
import com.itheima.pinda.file.dto.AttachmentRemoveDTO;
import com.itheima.pinda.file.dto.AttachmentResultDTO;
import com.itheima.pinda.file.dto.FilePageReqDTO;
import com.itheima.pinda.file.entity.Attachment;
import com.itheima.pinda.file.entity.File;
import com.itheima.pinda.file.properties.FileServerProperties;
import com.itheima.pinda.file.service.AttachmentService;
import com.itheima.pinda.file.strategy.FileStrategy;
import com.itheima.pinda.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 附件处理服务层实现类
 *
 * @author:ivan.liu
 */
@Service
@Slf4j
public class AttachmentServiceImpl extends ServiceImpl<AttachmentMapper, Attachment> implements AttachmentService {

    @Autowired
    private FileStrategy fileStrategy;

    /**
     * 用于生成业务id
     */
    @Autowired
    private IdGenerate<Long> idGenerate;

    /**
     * dozer用于属性赋值
     */
    @Autowired
    private DozerUtils dozerUtils;

    /**
     * 属性类
     */
    @Autowired
    private FileServerProperties fileServerProperties;

    @Autowired
    private FileBiz fileBiz;

    /**
     * 文件上传业务
     * @param multipartFile
     * @param bizId
     * @param bizType
     * @param isSingle
     * @param id
     * @return
     */
    @Override
    public AttachmentDTO upload(MultipartFile multipartFile, Long bizId, String bizType, Boolean isSingle, Long id) {
        String bizIdStr = null;
        //如果bizId为空则生成一个id
        if (bizId==null){
            bizIdStr = String.valueOf(idGenerate.generate());
        }else {
            bizIdStr = String.valueOf(bizId);
        }
        //调用策略类进行上传
        File file = fileStrategy.upload(multipartFile);

        //对象转换
        Attachment attachment = dozerUtils.map(file, Attachment.class);
        //设置未拷贝的属性
        attachment.setBizId(bizIdStr);
        attachment.setBizType(bizType);

        LocalDateTime now = LocalDateTime.now();
        attachment.setCreateMonth(DateUtils.formatAsYearMonth(now));
        attachment.setCreateWeek(DateUtils.formatAsYearWeek(now));
        attachment.setCreateDay(DateUtils.formatAsDateEn(now));

        //判断文件是否为单一文件
        if (isSingle){
            //将当前业务其他文件信息从数据库删除
            this.remove(Wrappers.<Attachment>lambdaQuery().eq(Attachment::getBizId,bizIdStr).eq(Attachment::getBizType,bizType));
        }

        if (id!=null&&id>0){
            //如果存在id，则为修改操作
            attachment.setId(id);
            this.updateById(attachment);
        }else {
            //否则为新增
            attachment.setId(idGenerate.generate());
            this.save(attachment);
        }

        return dozerUtils.map(attachment,AttachmentDTO.class);
    }

    /**
     * 根据ids删除附件
     * @param ids
     * @return
     */
    @Override
    public void delete(Long[] ids) {
        //健壮性检测
        if (ArrayUtils.isEmpty(ids)){
            //返回错误信息
            return ;
        }
        //查询数据库，得到文件路径
        List<Attachment> attachmentList = this.list(Wrappers.<Attachment>lambdaQuery()
                .in(Attachment::getId, ids)
        );
        List<FileDeleteDO> fileDeleteDOList = attachmentList.stream().map(attachment -> FileDeleteDO.builder()
                .relativePath(attachment.getRelativePath())
                .fileName(attachment.getFilename())
                .group(attachment.getGroup())
                .path(attachment.getPath())
                .build()
        ).collect(Collectors.toList());
        //删除具体文件
        fileStrategy.delete(fileDeleteDOList);

        //删除数据库
        this.removeByIds(Arrays.asList(ids));
    }

    /**
     * 根据业务id或业务类型删除
     * @param dto
     */
    @Override
    public void deleteByBiz(AttachmentRemoveDTO dto) {
        if (dto==null){
            return;
        }
        //如果为null,mp会自动取消拼接
        List<Attachment> attachmentList = this.list(Wrappers.<Attachment>lambdaQuery()
                .eq(Attachment::getBizId, dto.getBizId())
                .eq(Attachment::getBizType, dto.getBizType()));
        if(attachmentList.isEmpty()){
            return;
        }
        Long[] ids = attachmentList.stream().mapToLong(Attachment::getId).boxed().toArray(Long[]::new);
        //调用根据id删除
        this.delete(ids);
    }

    /**
     * 根据文件ids打包下载
     * @param ids
     */
    @Override
    public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids)  throws Exception  {
        List<Attachment> attachmentList = (List<Attachment>) this.listByIds(Arrays.asList(ids));
        down(request, response, attachmentList);
    }

    /**
     * 根据业务类型或业务id下载附件
     * @param bizIds
     * @param bizTypes
     * @param request
     * @param response
     * @throws Exception
     */
    @Override
    public void downloadByBiz(String[] bizIds, String[] bizTypes, HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<Attachment> attachmentList = this.list(Wrappers.<Attachment>lambdaQuery()
                .in(Attachment::getBizId, bizIds)
                .in(Attachment::getBizType, bizTypes)
        );
        down(request, response, attachmentList);
    }

    /**
     * 查询附件分页数据
     *
     * @param page
     * @param data
     * @return
     */
    @Override
    public IPage<Attachment> page(Page<Attachment> page, FilePageReqDTO data) {
        Attachment attachment = dozerUtils.map(data, Attachment.class);
        return this.page(page, Wraps.<Attachment>lbQ()
                .like(Attachment::getSubmittedFileName, attachment.getSubmittedFileName())
                .like(Attachment::getBizType, attachment.getBizType())
                .like(Attachment::getBizId, attachment.getBizId())
                .eq(Attachment::getDataType, attachment.getDataType())
                .orderByDesc(Attachment::getId)
        );
    }

    private void down(HttpServletRequest request, HttpServletResponse response, List<Attachment> attachmentList) throws Exception {
        if (attachmentList.isEmpty()) {
            throw BizException.wrap("下载的文件不存在");
        }
        //领域对象转换
        List<FileDO> fileDOList = attachmentList.stream().map(attachment -> FileDO.builder().submittedFileName(attachment.getSubmittedFileName())
                .size(attachment.getSize())
                .dataType(attachment.getDataType())
                .url(attachment.getUrl())
                .build()).collect(Collectors.toList());

        fileBiz.down(fileDOList, request, response);
    }

    /**
     * 根据业务类型和业务id查询附件
     *
     * @param bizTypes
     * @param bizIds
     * @return
     */
    public List<AttachmentResultDTO> find(String[] bizTypes, String[] bizIds) {
        return baseMapper.find(bizTypes, bizIds);
    }
}
